<!DOCTYPE html><html lang="zh-Hans"><head><meta name="generator" content="Hexo 3.8.0"><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0" name="viewport"><meta content="yes" name="apple-mobile-web-app-capable"><meta content="black-translucent" name="apple-mobile-web-app-status-bar-style"><meta content="telephone=no" name="format-detection"><meta name="description" content="漫漫IT路，持一颗恒心，带一份执着！"><title>菜鸟IT路 | dev-heaven</title><link rel="stylesheet" type="text/css" href="/css/style.css?v=0.0.0"><link rel="stylesheet" type="text/css" href="//lib.baomitu.com/normalize/8.0.1/normalize.min.css"><link rel="stylesheet" type="text/css" href="//lib.baomitu.com/pure/1.0.0/pure-min.css"><link rel="stylesheet" type="text/css" href="//lib.baomitu.com/pure/1.0.0/grids-responsive-min.css"><link rel="stylesheet" href="//lib.baomitu.com/font-awesome/4.7.0/css/font-awesome.min.css"><script type="text/javascript" src="//lib.baomitu.com/jquery/3.4.0/jquery.min.js"></script><link rel="icon" mask sizes="any" href="/favicon.ico"><link rel="Shortcut Icon" type="image/x-icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="apple-touch-icon-precomposed" href="/apple-touch-icon.png"></head><body><div class="body_container"><div id="header"><div class="site-name"><h1 class="hidden">菜鸟IT路</h1><a id="logo" href="/.">菜鸟IT路</a><p class="description">dev-heaven</p></div><div id="nav-menu"><a class="current" href="/."><i class="fa fa-home">Inicio</i></a><a href="/archives/"><i class="fa fa-archive"> Archivo</i></a><a href="/about/"><i class="fa fa-user"> Acerca de</i></a><a href="/atom.xml"><i class="fa fa-rss"> RSS</i></a></div></div><div class="pure-g" id="layout"><div class="pure-u-1 pure-u-md-3-4"><div class="content_container"><div class="post"><h1 class="post-title"><a href="/posts/36563.html">RabbitMQ从入门到精通---ACK机制</a></h1><div class="post-meta">2017-08-14</div><div class="post-content"><blockquote><p>首先需要思考一个问题：我们为什么要MQ？</p><ul><li>通过使用消息队列，我们可以异步处理请求，从而缓解系统的压力。</li><li>MQ提供松耦合的应用架构。</li></ul></blockquote><p>Consumer可能需要一段时间才能处理完收到的数据。如果在这个过程中，Consumer出错了，异常退出了，而数据还没有处理完成，这段数据就丢失了。如果我们采用no-ack的方式进行确认，也就是说，每次Consumer接到数据后，而不管是否处理完成，RabbitMQ Server会立即把这个Message标记为完成，然后从queue中删除了。</p><p>为了保证数据不被丢失，RabbitMQ支持消息确认机制，即ack。为了保证数据能被正确处理而不仅仅是被Consumer收到，我们就不能采用no-ack或者auto-ack，我们需要手动ack(manual-ack)。在数据处理完成后手动发送ack，这个时候Server才将Message删除。</p><h4 id="如何设置？"><a href="#如何设置？" class="headerlink" title="如何设置？"></a>如何设置？</h4><h5 id="action-in-java"><a href="#action-in-java" class="headerlink" title="action in java"></a>action in java</h5><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Channel.java，将autoAck设置为false即可。</span></span><br><span class="line">String <span class="keyword">basicConsume(String </span>queue, <span class="keyword">boolean </span>autoAck, Consumer callback)<span class="comment">;</span></span><br></pre></td></tr></table></figure><h5 id="action-in-spring"><a href="#action-in-spring" class="headerlink" title="action in spring"></a>action in spring</h5><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">rabbit:listener-container</span> <span class="attr">connection-factory</span>=<span class="string">"connectionFactory"</span> <span class="attr">acknowledge</span>=<span class="string">"manual"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">rabbit:listener</span> <span class="attr">ref</span>=<span class="string">"myConsumer"</span> <span class="attr">queues</span>=<span class="string">"myQueue"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">rabbit:listener-container</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="忘记ack？"><a href="#忘记ack？" class="headerlink" title="忘记ack？"></a>忘记ack？</h4><p>只能说后果很严重，因为没有ack的话，RabbitMQ Server会重新分发，并且RabbitMQ Server不会再发送数据给它（直至Server收到ack才会再次发送消息，参看测试用例），因为Server认为这个Consumer处理能力有限。这样就导致消息在RabbitMQ Server上堆积，最终造成内存泄露。</p><h5 id="如何排查"><a href="#如何排查" class="headerlink" title="如何排查"></a>如何排查</h5><p>可以使用以下命令查看没有被ACK的消息，如果有大量消息，那八九不离十就是忘记ack了。</p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo rabbitmqctl list_queues <span class="built_in">name</span> messages_ready messages_unacknowledged</span><br></pre></td></tr></table></figure><h4 id="ACK其他作用"><a href="#ACK其他作用" class="headerlink" title="ACK其他作用"></a>ACK其他作用</h4><p>ACK机制可以起到限流的作用，比如在消费者处理后，sleep一段时间，然后再ACK，这可以帮助消费者负载均衡。</p><p>当然，除了ACK，有两个比较重要的参数也在控制着consumer的load-balance，即prefetch和concurrency</p><h5 id="prefetch-amp-concurrency"><a href="#prefetch-amp-concurrency" class="headerlink" title="prefetch &amp; concurrency"></a>prefetch &amp; concurrency</h5><ol><li>prefetch: 每次从一次性从broker里面取的待消费的消息的个数。prefetch可以改变RabbitMQ的循环分发机制，比如设置其值为1，当消费者在处理消息完成，还没有ACK的时候，RabbitMQ的Server不会再发消息给该消费者。</li><li>concurrency: 这个表示每个listener创建多少个消费者（会创建多少个线程来消费）</li></ol><blockquote><ol><li>首先，basic.qos是针对channel进行设置的，也就是说只有在channel建立之后才能发送basic.qos信令。</li><li>其实basic.qos里还有另外两个参数可进行设置（global和prefetch_size），但RabbitMQ没有相应的实现。</li><li>RabbitMQ如何挑选消费者？当RabbitMQ要将队列中的一条消息投递给消费者时，会遍历该队列上的消费者列表，选一个合适的消费者，然后将消息投递出去。其中挑选消费者的一个依据就是看消费者对应的channel上未ack的消息数是否达到设置的prefetch_count个数，如果未ack的消息数达到了prefetch_count的个数，则不符合要求。</li></ol></blockquote><h4 id="ACK测试"><a href="#ACK测试" class="headerlink" title="ACK测试"></a>ACK测试</h4><h5 id="case1：没有ACK"><a href="#case1：没有ACK" class="headerlink" title="case1：没有ACK"></a>case1：没有ACK</h5><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Channel channel = connection.createChannel();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 消费者</span></span><br><span class="line">DefaultConsumer consumer = <span class="keyword">new</span> DefaultConsumer(channel) &#123;</span><br><span class="line">    @Override</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> handleDelivery(<span class="keyword">String</span> consumerTag, Envelope envelope, AMQP.BasicProperties properties, <span class="built_in">byte</span>[] body) <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="keyword">String</span> message = <span class="keyword">new</span> <span class="keyword">String</span>(body, <span class="string">"UTF-8"</span>);</span><br><span class="line">        System.out.<span class="built_in">println</span>(<span class="string">"1号消费者 ===&gt; "</span> + message);</span><br><span class="line">        <span class="comment">//getChannel().basicAck(envelope.getDeliveryTag(), false);</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">channel.basicConsume(QUEUE_NAME, <span class="keyword">false</span>, <span class="keyword">null</span>, consumer);</span><br></pre></td></tr></table></figure><blockquote><p>结论：没有ACK，消费者任然会正常消费，消费者断开重连后，RabbitMQ Server会重新将消息发送给消费者，消费者重新执行任务，这可能导致<strong>#重复消费#</strong>。</p></blockquote><h5 id="case2：两个消费者，一个正常ACK，一个不ACK"><a href="#case2：两个消费者，一个正常ACK，一个不ACK" class="headerlink" title="case2：两个消费者，一个正常ACK，一个不ACK"></a>case2：两个消费者，一个正常ACK，一个不ACK</h5><blockquote><p>两个消费者请使用两个Java实例运行</p></blockquote><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">Channel channel = connection.createChannel();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 消费者</span></span><br><span class="line">DefaultConsumer consumer = <span class="keyword">new</span> DefaultConsumer(channel) &#123;</span><br><span class="line">    @Override</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> handleDelivery(<span class="keyword">String</span> consumerTag, Envelope envelope, AMQP.BasicProperties properties, <span class="built_in">byte</span>[] body) <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="keyword">String</span> message = <span class="keyword">new</span> <span class="keyword">String</span>(body, <span class="string">"UTF-8"</span>);</span><br><span class="line">        System.out.<span class="built_in">println</span>(<span class="string">"1号消费者 ===&gt; "</span> + message);</span><br><span class="line">        <span class="comment">// Thread.sleep(2000); // 延时ACK</span></span><br><span class="line">        getChannel().basicAck(envelope.getDeliveryTag(), <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">channel.basicConsume(QUEUE_NAME, <span class="keyword">false</span>, <span class="keyword">null</span>, consumer);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 消费者</span></span><br><span class="line">DefaultConsumer anotherConsumer = <span class="keyword">new</span> DefaultConsumer(channel) &#123;</span><br><span class="line">    @Override</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> handleDelivery(<span class="keyword">String</span> consumerTag, Envelope envelope, AMQP.BasicProperties properties, <span class="built_in">byte</span>[] body) <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="keyword">String</span> message = <span class="keyword">new</span> <span class="keyword">String</span>(body, <span class="string">"UTF-8"</span>);</span><br><span class="line">        System.out.<span class="built_in">println</span>(<span class="string">"2号消费者 ===&gt; "</span> + message);</span><br><span class="line">        <span class="comment">//getChannel().basicAck(envelope.getDeliveryTag(), false);</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">channel.basicConsume(QUEUE_NAME, <span class="keyword">false</span>, <span class="keyword">null</span>, anotherConsumer);</span><br></pre></td></tr></table></figure><blockquote><p>结论：测试发现，两个消费者交替消费（<strong>循环分发机制</strong>）。</p><ol><li>重启2号消费者，这个时候RabbitMQ会将其消费过的消息重新发给1号消费者消费。</li><li>即使没有ACK，RabbitMQ也不是立即不发送消息到没有ack的consumer，其实这很好理解，既然ack可以延时，那Server完全有理由相信consumer回复只是比较慢而已，它不是不回。所以继续会发第二条。</li><li>本来以为是2号消费者重启过程中，1号消费者消费过快，等2号消费者起起来时，消息已经被1号消费者消费完，其实不是，就算给1号消费者加上延迟ACK，2号消费者也不会再接收消息。应该是RabbitMQ内部的某种记忆功能，将2号消费者没有ACK的消息，直接归给1号消费者消费。</li><li>再次执行生产者，此时2号消费者任然可以重新接收消息。</li><li>设置1号消费者prefetch=1(<code>channel.basicQos(1)</code>)，2号消费者不做任何设置，然后两个消费者都订阅同一队列，开启acknowledge机制。RabbitMQ向1号消费者投递了一条消息后，消费者未对该消息进行ack，RabbitMQ不会再向该消费者投递消息，剩下的消息均投递给了2号消费者。这和第二个试验结果不同。</li></ol></blockquote><h3 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h3><blockquote><p>我们知道，有了ACK机制，当消费者挂掉后，消息可以不丢失，但是如果RabbitMQ Server挂掉了呢？这就需要持久化机制。如果没有设置相应的持久化，会在Server退出后丢掉Exchange和Queue。</p></blockquote><h4 id="队列持久化"><a href="#队列持久化" class="headerlink" title="队列持久化"></a>队列持久化</h4><figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @param durable持久化</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="built_in">Queue</span>.DeclareOk queueDeclare(<span class="built_in">String</span> <span class="built_in">queue</span>, <span class="built_in">boolean</span> durable, <span class="built_in">boolean</span> exclusive, <span class="built_in">boolean</span> autoDelete,</span><br><span class="line">                                 <span class="built_in">Map</span>&lt;<span class="built_in">String</span>, Object&gt; arguments) throws IOException;</span><br></pre></td></tr></table></figure><h4 id="Exchange持久化"><a href="#Exchange持久化" class="headerlink" title="Exchange持久化"></a>Exchange持久化</h4><figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @param durable持久化</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Exchange.DeclareOk exchangeDeclare(<span class="built_in">String</span> exchange, <span class="built_in">String</span> <span class="keyword">type</span>, <span class="built_in">boolean</span> durable) throws IOException;</span><br></pre></td></tr></table></figure><h4 id="Message持久化"><a href="#Message持久化" class="headerlink" title="Message持久化"></a>Message持久化</h4><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">BasicProperties </span>properties = new AMQP.<span class="keyword">BasicProperties.Builder()</span></span><br><span class="line"><span class="keyword"> </span>               .deliveryMode(<span class="number">2</span>).<span class="keyword">build(); </span></span><br><span class="line">channel.<span class="keyword">basicPublish(EXCHANGE_NAME, </span>QUEUE_NAME, properties, message.getBytes(<span class="string">"UTF-8"</span>))<span class="comment">;</span></span><br></pre></td></tr></table></figure><blockquote><p>引入spring-rabbit</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&gt; <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">&gt;    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.amqp<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">&gt;    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-rabbit<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">&gt;    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.7.3.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">&gt; <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">&gt;</span><br></pre></td></tr></table></figure><p>其中有一个类<code>org.springframework.amqp.core.MessageDeliveryMode</code>表示消息发布模式，参看toInt方法，可以看出：非持久化（1）和持久化（2）。</p></blockquote><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ol><li><a href="https://my.oschina.net/hncscwc/blog/195560" target="_blank" rel="noopener">https://my.oschina.net/hncscwc/blog/195560</a></li><li><a href="https://emacsist.github.io/2015/12/18/%E5%BD%BB%E5%BA%95%E4%BA%86%E8%A7%A3Spring-%E4%B8%AD-RabbitMQ%E9%85%8D%E7%BD%AE%E7%9A%84concurrency-%E5%92%8C-task-executor%EF%BC%88%E5%BC%82%E6%AD%A5/" target="_blank" rel="noopener">https://emacsist.github.io/2015/12/18/%E5%BD%BB%E5%BA%95%E4%BA%86%E8%A7%A3Spring-%E4%B8%AD-RabbitMQ%E9%85%8D%E7%BD%AE%E7%9A%84concurrency-%E5%92%8C-task-executor%EF%BC%88%E5%BC%82%E6%AD%A5/</a></li><li><a href="http://emacsist.github.io/2015/12/14/Spring-%E4%B8%8E-RabbitMQ-%E7%BB%93%E5%90%88%E9%85%8D%E7%BD%AE%E4%BB%A5%E5%8F%8A%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/" target="_blank" rel="noopener">http://emacsist.github.io/2015/12/14/Spring-%E4%B8%8E-RabbitMQ-%E7%BB%93%E5%90%88%E9%85%8D%E7%BD%AE%E4%BB%A5%E5%8F%8A%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/</a></li></ol></div><p class="readmore"><a href="/posts/36563.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/17212.html">RabbitMQ从入门到精通---小试牛刀</a></h1><div class="post-meta">2017-08-10</div><div class="post-content"><blockquote><p>前面说了基本概念，也尝试动手安装了RabbitMQ Server，接下来我觉得改动动手Coding了，毕竟老话说得好，“光说不练假把式”，不能仅停留在理论上，嗯，要动手！！！</p></blockquote><h3 id="Get-Started"><a href="#Get-Started" class="headerlink" title="Get Started"></a>Get Started</h3><p>新建maven项目，巴拉巴拉…（一杯咖啡的功夫）maven，项目已经建好了，引入rabbit操作的jar包。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.rabbitmq<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>amqp-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.2.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="Get-Started-1"><a href="#Get-Started-1" class="headerlink" title="Get Started"></a>Get Started</h4><blockquote><p>不多说，直接上代码！</p></blockquote><h5 id="生产者"><a href="#生产者" class="headerlink" title="生产者"></a>生产者</h5><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="built_in">String</span> QUEUE_NAME = <span class="string">"myQueue"</span>;</span><br><span class="line"><span class="keyword">final</span> <span class="built_in">String</span> EXCHANGE_NAME = <span class="string">"myExchange"</span>;</span><br><span class="line"><span class="keyword">final</span> <span class="built_in">String</span> ROUTING_KEY = <span class="string">"myRoutingKey"</span>;</span><br><span class="line"></span><br><span class="line">ConnectionFactory <span class="keyword">factory</span> = <span class="keyword">new</span> ConnectionFactory();</span><br><span class="line"><span class="comment">// 默认localhost</span></span><br><span class="line"><span class="keyword">factory</span>.setHost(<span class="string">"localhost"</span>);</span><br><span class="line"><span class="comment">// 默认5672</span></span><br><span class="line"><span class="keyword">factory</span>.setPort(<span class="number">5672</span>);</span><br><span class="line"><span class="keyword">factory</span>.setUsername(<span class="string">"admin"</span>);</span><br><span class="line"><span class="keyword">factory</span>.setPassword(<span class="string">"huweitech"</span>);</span><br><span class="line"><span class="comment">// 创建连接</span></span><br><span class="line">Connection connection = <span class="keyword">factory</span>.newConnection();</span><br><span class="line">Channel channel = connection.createChannel();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 声明Exchange</span></span><br><span class="line">channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);</span><br><span class="line"><span class="comment">// 声明队列</span></span><br><span class="line">channel.queueDeclare(QUEUE_NAME, <span class="keyword">false</span>, <span class="keyword">false</span>, <span class="keyword">false</span>, <span class="keyword">null</span>);</span><br><span class="line"><span class="comment">// 绑定队列和Exchange</span></span><br><span class="line">channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);</span><br><span class="line"></span><br><span class="line"><span class="built_in">String</span> message = <span class="string">"hello world"</span>;</span><br><span class="line">channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, <span class="keyword">null</span>, message.getBytes(<span class="string">"UTF-8"</span>));</span><br><span class="line"><span class="comment">// 关闭channel和连接</span></span><br><span class="line">channel.close();</span><br><span class="line">connection.close();</span><br><span class="line">``` </span><br><span class="line"></span><br><span class="line">执行代码后在RabbitMQ的web控制台上可以看到</span><br><span class="line">![](https:<span class="comment">//static.dev-heaven.com/images/Control%20Panel.png)</span></span><br><span class="line"></span><br><span class="line">每次执行会发出一条消息，然后close掉连接。</span><br><span class="line"></span><br><span class="line">#### 消费者</span><br></pre></td></tr></table></figure><p>final String QUEUE_NAME = “myQueue”;</p><p>ConnectionFactory factory = new ConnectionFactory();<br>// 默认localhost<br>factory.setHost(“localhost”);<br>// 默认5672<br>factory.setPort(5672);<br>factory.setUsername(“admin”);<br>factory.setPassword(“huweitech”);<br>// 创建连接<br>Connection connection = factory.newConnection();<br>final Channel channel = connection.createChannel();</p><p>DefaultConsumer consumer = new DefaultConsumer(channel) {<br>@Override<br>public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {<br>String message = new String(body, “UTF-8”);<br>System.out.println(message);<br>channel.basicAck(envelope.getDeliveryTag(), false);<br>}<br>};<br>channel.basicConsume(QUEUE_NAME, false, consumer);<br></p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">执行代码后可以在<span class="selector-tag">RabbitMQ</span>的<span class="selector-tag">web</span>控制台上可以看到</span><br><span class="line">!<span class="selector-attr">[]</span>(<span class="attribute">https</span>:<span class="comment">//static.dev-heaven.com/images/Consumer.png)</span></span><br><span class="line"></span><br><span class="line">消息被消费，然后给RabbitMQ Server发送ack（<span class="built_in">`channel.basicAck(envelope.getDeliveryTag(), false);`</span>），确认消费。然后程序会挂起，继续等待接收消息。</span><br><span class="line"></span><br><span class="line">*PS：关于ACK，在下篇博客中说明。*</span><br><span class="line"></span><br><span class="line">以上就是简单的生产者消费者，其实可以更简单，在生产者中，其实可以不声明Exchange，改为如下：</span><br></pre></td></tr></table></figure><p></p><p>final String QUEUE_NAME = “myQueue”;</p><p>ConnectionFactory factory = new ConnectionFactory();<br>// 默认localhost<br>factory.setHost(“localhost”);<br>// 默认5672<br>factory.setPort(5672);<br>factory.setUsername(“admin”);<br>factory.setPassword(“huweitech”);<br>// 创建连接<br>Connection connection = factory.newConnection();<br>Channel channel = connection.createChannel();</p><p>// 声明队列<br>channel.queueDeclare(QUEUE_NAME, false, false, false, null);</p><p>String message = “hello world2211112222”;<br>channel.basicPublish(“”, QUEUE_NAME, null, message.getBytes(“UTF-8”));</p><p>channel.close();<br>connection.close();<br></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">？？？可以不直接声明Exchange？你是不是骗我啊，明明说好的生产者发送到Exchange的啊！</span><br><span class="line"></span><br><span class="line">&gt; RabbitMQ默认有一个Exchange，叫default exchange，它用一个空字符串表示，它是direct exchange类型，RabbitMQ管理控制台对这个default exchange做了描述：The<span class="built_in"> default </span>exchange is implicitly bound <span class="keyword">to</span> every queue, with a<span class="built_in"> routing </span>key equal <span class="keyword">to</span> the<span class="built_in"> queue </span>name. It is <span class="keyword">not</span> possible <span class="keyword">to</span> explicitly bind <span class="keyword">to</span>, <span class="keyword">or</span> unbind <span class="keyword">from</span> the<span class="built_in"> default </span>exchange. It also cannot be deleted.</span><br><span class="line"></span><br><span class="line"><span class="comment">### Channel</span></span><br><span class="line"></span><br><span class="line">&gt; 这里想说的是Channel类中的方法以及用法</span><br><span class="line"></span><br><span class="line"><span class="comment">#### basicQos</span></span><br><span class="line"></span><br><span class="line">&gt; prefetchCount：会告诉RabbitMQ不要同时给一个消费者推送多于N个消息，即一旦有N个消息还没有ack，则该consumer将block掉，直到有消息ack</span><br><span class="line">备注：据说prefetchSize 和global这两项，rabbitmq没有实现，暂且不研究</span><br></pre></td></tr></table></figure><p></p><p>void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;<br></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">#### basicPublish</span><br><span class="line"></span><br><span class="line">&gt; </span><br><span class="line">* routingKey：路由键，#匹配<span class="number">0</span>个或多个单词，*匹配一个单词，在topic exchange做消息转发用</span><br><span class="line">* mandatory：<span class="literal">true</span>：如果exchange根据自身类型和消息routeKey无法找到一个符合条件的<span class="built_in">queue</span>，那么会调用basic.<span class="keyword">return</span>方法将消息返还给生产者。<span class="literal">false</span>：出现上述情形broker会直接将消息扔掉</span><br><span class="line">* immediate：<span class="literal">true</span>：如果exchange在将消息route到<span class="built_in">queue</span>(s)时发现对应的<span class="built_in">queue</span>上没有消费者，那么这条消息不会放入队列中。当与消息routeKey关联的所有<span class="built_in">queue</span>(一个或多个)都没有消费者时，该消息会通过basic.<span class="keyword">return</span>方法返还给生产者。</span><br><span class="line">* BasicProperties ：需要注意的是BasicProperties.deliveryMode，<span class="number">0</span>:不持久化 <span class="number">1</span>：持久化 这里指的是消息的持久化，配合channel(durable=<span class="literal">true</span>),<span class="built_in">queue</span>(durable=<span class="literal">true</span>)可以实现，即使服务器宕机，消息仍然保留</span><br><span class="line">简单来说：mandatory标志告诉服务器至少将该消息路由到一个队列中，否则将消息返还给生产者；immediate标志告诉服务器如果该消息关联的<span class="built_in">queue</span>上有消费者，则马上将消息投递给它，如果所有<span class="built_in">queue</span>都没有消费者，直接把消息返还给生产者，不用将消息入队列等待消费者了。</span><br></pre></td></tr></table></figure><p></p><p>void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body)<br>throws IOException;<br></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">### queueDeclare</span></span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> 声明队列</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * durable: 是否持久化，服务器宕机后能保存下来。</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * exclusive：是否为当前连接的专用队列，在连接断开后，会自动删除该队列。</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * autoDelete：消费者断开后，会自动删除该队列</span></span><br></pre></td></tr></table></figure><p></p><p>Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,<br>Map<string , object>arguments) throws IOException;<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">### exchangeDeclare</span></span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> 声明Exchange，四种类型：BuiltinExchangeType &#123; DIRECT(<span class="string">"direct"</span>), FANOUT(<span class="string">"fanout"</span>), TOPIC(<span class="string">"topic"</span>), HEADERS(<span class="string">"headers"</span>)&#125;</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * durable: 服务器重启会保留下来Exchange。警告：仅设置此选项，不代表消息持久化。即不保证重启后消息还在。消息publish的时候需要使用deliveryMode=2</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * autoDelete: 当已经没有消费者时，服务器是否可以删除该Exchange。</span></span><br></pre></td></tr></table></figure></string></p><p>Exchange.DeclareOk exchangeDeclare(String exchange,<br>BuiltinExchangeType type,<br>boolean durable,<br>boolean autoDelete,<br>boolean internal,<br>Map<string , object>arguments) throws IOException;<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">### basicAck</span></span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> 当消息正常被消费后，发送ack标识通知Server。</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * deliveryTag: 可以理解为消息在server中的唯一索引（或者叫id）</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * multiple：是否批量.<span class="literal">true</span>:将一次性ack所有小于deliveryTag的消息。</span></span><br></pre></td></tr></table></figure></string></p><p>void basicAck(long deliveryTag, boolean multiple) throws IOException;<br></p><figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">#### basicNack</span><br><span class="line"></span><br><span class="line">&gt; 拒收Message</span><br><span class="line">&gt; requeue: 是否重新入队</span><br></pre></td></tr></table></figure><p></p><p>void basicNack(long deliveryTag, boolean multiple, boolean requeue)<br>throws IOException;<br></p><figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">#### basicReject</span><br><span class="line"></span><br><span class="line">&gt; 拒收Message，和basicNack不同的是，每次只能拒收一条消息。</span><br></pre></td></tr></table></figure><p></p><p>void basicReject(long deliveryTag, boolean requeue) throws IOException;<br></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">### basicConsume</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * autoAck：是否自动ack，如果不自动ack，需要使用channel.ack、channel.nack、channel.basicReject 进行消息应答</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * consumerTag：Server为消费者生成的一个标识</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * noLocal：如果只为<span class="literal">true</span>，则服务器不能将此channel上publish的Message发给该消费者。</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> * exclusive：排他，即只能单个消费者消费队列里的消息，适用于必须严格执行消息队列的消费顺序（先进先出）。</span></span><br></pre></td></tr></table></figure><p></p><p>String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map<string , object>arguments, Consumer callback) throws IOException;<br><figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">#### queueBind</span><br><span class="line"></span><br><span class="line">&gt; 将消费者和队列绑定</span><br></pre></td></tr></table></figure></string></p><p>Queue.BindOk queueBind(String queue, String exchange, String routingKey, Map<string , object>arguments) throws IOException;<br>```</string></p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://yq.aliyun.com/articles/73672" target="_blank" rel="noopener">https://yq.aliyun.com/articles/73672</a></li><li><a href="http://www.cnblogs.com/youzhongmin/p/6940377.html" target="_blank" rel="noopener">http://www.cnblogs.com/youzhongmin/p/6940377.html</a></li></ul></div><p class="readmore"><a href="/posts/17212.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/43318.html">RabbitMQ从入门到精通---基础概念</a></h1><div class="post-meta">2017-08-09</div><div class="post-content"><blockquote><p>上面我们说了<a href="https://dev-heaven.com/2017/05/08/RabbitMQ%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A%E2%80%94%E5%AE%89%E8%A3%85/" target="_blank" rel="noopener">RabbitMQ及其插件安装过程</a>，算是动手入门了，这次我们来说说RabbitMQ基础概念，夯实理论基础。</p></blockquote><h3 id="RabbitMQ结构"><a href="#RabbitMQ结构" class="headerlink" title="RabbitMQ结构"></a>RabbitMQ结构</h3><p>首先我们来看看RabbitMQ的结构</p><p><img src="https://static.dev-heaven.com/images/MQ%20Construction.png" alt></p><h4 id="RabbitMQ-Server"><a href="#RabbitMQ-Server" class="headerlink" title="RabbitMQ Server"></a>RabbitMQ Server</h4><p>也叫Broker，它就是维护一条从Producer到Consumer的路线，保证数据能够按照指定的方式进行传输。</p><h4 id="生产者（Producer）"><a href="#生产者（Producer）" class="headerlink" title="生产者（Producer）"></a>生产者（Producer）</h4><p>图中ClientA &amp; B就是生产者，它们负责生产Message（消息），每一条Message由有效载荷（Payload）和标签（label组成），payload顾名思义就是传输的数据。label是exchange的名字或者说是一个tag，它描述了payload，而且RabbitMQ也是通过这个label来决定把这个Message发给哪个Consumer。</p><h4 id="消费者（Consumer）"><a href="#消费者（Consumer）" class="headerlink" title="消费者（Consumer）"></a>消费者（Consumer）</h4><p>图中Client1 &amp; 2 &amp; 3均是消费者，他们接收Message，此时Message只有payload，label已经被删除。</p><h4 id="交换机（Exchange）"><a href="#交换机（Exchange）" class="headerlink" title="交换机（Exchange）"></a>交换机（Exchange）</h4><p>接收生产者发来的消息，并根据规则进行路由。有三种常用类型的Exchanges：direct, fanout, topic，还有一种不常用的headers类型。 每个实现了不同的路由算法（routing algorithm）。生产者将消息发送到Exchange（交换器），由Exchange将消息路由到一个或多个Queue中（或者丢弃，当Queue不存在）。</p><ul><li><p>Direct Exchange: 如果 routing key 匹配, 那么Message就会被传递到相应的Queue中。其实在queue创建时，它会自动的以queue的名字作为routing key来绑定那个Exchange。</p></li><li><p>Fanout Exchange: 会向响应的queue广播。</p></li><li><p>Topic Exchange: 对key进行模式匹配，比如ab*可以传递到所有ab*的Queue，<em>匹配一个单词，#匹配0或多个单词，所以声明bingdingKey的时候，请使用“.”进行分割。当routingKey为“#”时，TopicExchange变为FanoutExchange，当routingKey不包含“\</em>”或“#”时，它就变成了DirectExchange。</p></li><li><p>Headers Exchange: headers类型的Exchange不依赖于routing key与binding key的匹配规则来路由消息，而是根据发送的消息内容中的headers属性进行匹配。在绑定Queue与Exchange时指定一组键值对；当消息发送到Exchange时，RabbitMQ会取到该消息的headers（也是一个键值对的形式），对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对；如果完全匹配则消息会路由到该Queue，否则不会路由到该Queue。</p></li></ul><h4 id="队列（Queue）"><a href="#队列（Queue）" class="headerlink" title="队列（Queue）"></a>队列（Queue）</h4><p>消息消费前存放的地方，</p><h4 id="路由键（Routing-key）和绑定键（Binding-key）"><a href="#路由键（Routing-key）和绑定键（Binding-key）" class="headerlink" title="路由键（Routing key）和绑定键（Binding key）"></a><em>路由键（Routing key）和绑定键（Binding key）</em></h4><blockquote><p>其实二者是指一个东西，只是逻辑上不太一样。</p></blockquote><p>队列（Queue）通过BindingKey和交换机（Exchange），而消息（Message）发送的时候需要指定RoutingKey，交换机将会对绑定键（binding key）和路由键（routing key）进行精确匹配，从而确定消息该分发到哪个队列。</p><blockquote><p>生产者发送消息的时候，需要指定exchange和routingKey，才能找到对用的exchange，然后通过routingKey找到相应的队列。如果不指定exchange，消息会发往rabbitMQ默认的名称为“”的exchange，这个exchange默认被绑定到任何队列上，此时如果不指定routingKey或routingKey不等于任何队列的名字，该消息会被丢弃，否则被发往名称和routingKey相同的队列。</p></blockquote><h4 id="Virtual-hosts"><a href="#Virtual-hosts" class="headerlink" title="Virtual hosts"></a>Virtual hosts</h4><p>虚拟主机，每个virtual host本质上都是一个RabbitMQ Server，拥有它自己的queue，exchagne，和bings rule等等。这保证了你可以在多个不同的application中使用RabbitMQ。</p><h3 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h3><blockquote><p>摘自anzhsoft博客的三个问题，留作思考。</p><ol><li>Will “*“ binding catch a message sent with an empty routing key?</li><li>Will “#.*“ catch a message with a string “..” as a key? Will it catch a message with a single word key?</li><li>How different is “a.*.#” from “a.#”?</li></ol></blockquote><ul><li><a href="http://blog.csdn.net/anzhsoft/article/details/19563091" target="_blank" rel="noopener">http://blog.csdn.net/anzhsoft/article/details/19563091</a></li><li><a href="http://wiki.jikexueyuan.com/project/rabbitmq/routing.html" target="_blank" rel="noopener">http://wiki.jikexueyuan.com/project/rabbitmq/routing.html</a></li></ul></div><p class="readmore"><a href="/posts/43318.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/3715.html">MySQL之charset和collation</a></h1><div class="post-meta">2017-08-09</div><div class="post-content"><blockquote><p>使用 mysql 创建数据表的时候， 总免不了要涉及到 character set 和 collation 的概念， 之前不是很了解。今天朋友突然问到，就自己查了下整理下。</p></blockquote><h3 id="character-set和collation"><a href="#character-set和collation" class="headerlink" title="character set和collation"></a>character set和collation</h3><h4 id="character-set"><a href="#character-set" class="headerlink" title="character set"></a>character set</h4><p>character set， 即字符集，常见的有utf-8，Unicode，GB2312</p><h5 id="UTF8，Unicode，GB2312区分与联系"><a href="#UTF8，Unicode，GB2312区分与联系" class="headerlink" title="UTF8，Unicode，GB2312区分与联系"></a>UTF8，Unicode，GB2312区分与联系</h5><ul><li>Unicode：统一字符编码，让世界上所有字符都对应一个编码。</li><li>UTF8：Unicode最初设计想用2个字节表示所有字符，但是最多只能表示65535个，这个称为UCS-2，当字符数超过这个范围，就需要用四个字节来表示了，即UCS-4。但是早期的人类缺乏先见之明，只考虑了ASCII码。单字节的0被当做了一种非常特殊的字符：字符串终结符。如果采用UCS-2或者UCS-4，Unix中很多工具将无法使用。为了解决这个问题，就需要采用一些编码技术，把Unicode映射到一个不包含0byte的编码空间中，绕开0这个东西，目标编码，就是UTF8。</li><li>GB2312：它是和Unicode地位相等的。也就是说，它和UTF8、Unicode没有任何关系。它主要针对中文字符。</li></ul><blockquote><ul><li>1.UCS代表Universal Character Set通用字符集</li><li>2.UTF代表UCS Transformation Format</li></ul></blockquote><h6 id="mysql-有两个支持-unicode-的-character-set"><a href="#mysql-有两个支持-unicode-的-character-set" class="headerlink" title="mysql 有两个支持 unicode 的 character set:"></a>mysql 有两个支持 unicode 的 character set:</h6><ol><li>ucs2: 使用 16 bits 来表示一个 unicode 字符。</li><li>utf8: 使用 1~3 bytes 来表示一个 unicode 字符。</li></ol><h4 id="collation"><a href="#collation" class="headerlink" title="collation"></a>collation</h4><p>collation, 即比对方法。用于指定数据集如何排序，以及字符串的比对规则。</p><h5 id="Collation相关概念"><a href="#Collation相关概念" class="headerlink" title="Collation相关概念"></a>Collation相关概念</h5><ol><li><p>ci： case insensitive，比较的时候不区分大小写</p><blockquote><h4 id="在-ci-的-collation-下，如何在比对时区分大小写"><a href="#在-ci-的-collation-下，如何在比对时区分大小写" class="headerlink" title="在 ci 的 collation 下，如何在比对时区分大小写?"></a>在 ci 的 collation 下，如何在比对时区分大小写?<br></h4><p>推荐使用</p><pre><code><figure class="highlight n1ql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; <span class="keyword">select</span> * <span class="keyword">from</span> pet <span class="keyword">where</span> name = <span class="keyword">binary</span> <span class="string">'whistler'</span>;</span><br><span class="line">```这样可以保证当前字段的索引依然有效， 而</span><br></pre></td></tr></table></figure></code></pre></blockquote></li></ol></div><p class="readmore"><a href="/posts/3715.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/20342.html">Nginx log缓存</a></h1><div class="post-meta">2017-07-18</div><div class="post-content"><p>调试阶段，可以禁用log缓存</p><p>对access_log缓存进行设置</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">open_log_file_cache</span> <span class="literal">off</span>;</span><br></pre></td></tr></table></figure></div><p class="readmore"><a href="/posts/20342.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/55163.html">穷人也能拥抱‘持续集成’与‘持续交付’</a></h1><div class="post-meta">2017-07-16</div><div class="post-content"><p>文章首发：<a href="https://dev-heaven.com/2017/07/16/%E7%A9%B7%E4%BA%BA%E4%B9%9F%E8%83%BD%E6%8B%A5%E6%8A%B1%E2%80%98%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90%E2%80%99%E4%B8%8E%E2%80%98%E6%8C%81%E7%BB%AD%E4%BA%A4%E4%BB%98%E2%80%99/" target="_blank" rel="noopener">https://www.dev-heaven.com</a><br>如果有问题请加QQ讨论群：656206901</p><blockquote><p>首先说一说为什么说穷人，笔者自己买了一台阿里云的ECS服务器，最开始是一台便宜的单核1G的服务器（其实前面还有更苦的，一把辛酸泪就不说了），苦了它了，里面跑了一堆服务，诸如Tomcat、Nginx、MySQL、Redis、RabbitMQ、JVM等等，发现本来就很吃力，后来又想搞个持续集成部署个Jenkins，结果搞挂了几次服务器，每次内存飙到95%(要知道Gitlab要求机器最好为4G)，只能重启服务器了。又没钱升级配置，只能看看有什么服务比较好。</p></blockquote><h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><h4 id="什么是持续交付与持续集成？"><a href="#什么是持续交付与持续集成？" class="headerlink" title="什么是持续交付与持续集成？"></a>什么是持续交付与持续集成？</h4><p>持续交付所描述的软件开发，是从原始需求识别到 最终产品部署到生产环境这个过程中，需求以小批量形式在团队的各个角色间顺 畅流动，能够以较短地周期完成需求的小粒度频繁交付。频繁的交付周期带来了 更迅速的对软件的反馈，并且在这个过程中，需求分析、产品的用户体验和交互 设计、开发、测试、运维等角色密切协作，相比于传统的瀑布式软件团队，更少 浪费。</p><p>持续集成是一种软件开发实践，即团队开发成员经常集成他们的工作，通过每个成员每天至少集成一次，也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建（包括编译，发布，自动化测试）来验证，从而尽早地发现集成错误。</p><h3 id="基本构建"><a href="#基本构建" class="headerlink" title="基本构建"></a>基本构建</h3><ul><li>版本控制：可以简单的理解为代码管理（SCM），如常见的GitHub、GitOSC、GitLab等。</li><li>持续集成CI：各种CI工具，如Jenkins、gitlab-ci等</li><li>自动化测试</li><li>构建工具及构建脚本</li><li>部署上线</li></ul><h3 id="穷人的持续集成"><a href="#穷人的持续集成" class="headerlink" title="穷人的持续集成"></a>穷人的持续集成</h3><p>今天的主角就是阿里云的CRP和OSCGit，先说说开源中国的git服务，它是完全免费的，允许开发者使用私有库托管代码，不是GitHub 7刀每月。另一个就是阿里云的CRP，免费的在线编译打包工具，当然CRP目前是公测，公测了好久了，说不定什么时候就收费了，OSCHINA红薯说他们马上会搞个免费的了。</p><h4 id="CRP"><a href="#CRP" class="headerlink" title="CRP"></a><a href="https://crp.aliyun.com" target="_blank" rel="noopener">CRP</a></h4><blockquote><p>给你私有、安全的代码托管服务。集合编译、测试、发布等插件。支持所有Git命令，兼容现有Git工具。私有Git仓库，存储任何类型的源代码及文件。在线浏览和管理代码，提升研发效率。</p></blockquote><h5 id="如何使用？"><a href="#如何使用？" class="headerlink" title="如何使用？"></a>如何使用？</h5><ol><li>新建工作流<br><img src="https://static.dev-heaven.com/images/crp1.png" alt></li><li>选择工作量模板<br><img src="https://static.dev-heaven.com/images/crp2.png" alt></li><li>配置工作流<br><img src="https://static.dev-heaven.com/images/crp3.png" alt></li><li><p>根据提示创建代码仓库</p><blockquote><p>笔者这里使用的是OSCGit托管代码的，但是CRP只能从阿里code中拉取代码，所以难题就是怎么打通阿里code和OSCGit，让提交到OSCGit的代码怎么同步到阿里code，自建Git服务器可以使用GitHooks，但是OSCGit，以及GitHub只提供了Webhooks，后面我们在来说怎么使用WebHook同步代码。</p></blockquote></li><li>小的注意点<blockquote><ol><li>crp中‘编译/测试’环节，可以去掉mvn test以节约时间</li><li>物理输出路径必须填写，否则可能无法部署，可以直接填 <code>target/</code></li></ol></blockquote></li><li>部署配置<br><img src="https://static.dev-heaven.com/images/crp4.png" alt><br>在配置部署前需要配置目标机器，配置好才会出现如图所示的配置。</li><li><p>配置部署组<br><img src="https://static.dev-heaven.com/images/crp5.png" alt></p><blockquote><p>添加机器，请选择直接输入机器列表，这样方便点，注意需要支持公网SSH。</p></blockquote><p><img src="https://static.dev-heaven.com/images/crp6.png" alt></p></li><li>剩下比较简单，就不再赘述了。</li></ol><h3 id="如何同步OSCGit代码到阿里code"><a href="#如何同步OSCGit代码到阿里code" class="headerlink" title="如何同步OSCGit代码到阿里code"></a>如何同步OSCGit代码到<a href="https://code.aliyun.com" target="_blank" rel="noopener">阿里code</a></h3><blockquote><p>使用OSCGit提供的WebHooks，如图<br><img src="https://static.dev-heaven.com/images/crp7.png" alt></p></blockquote><p>提供一个post接口，将接收osc git server 推送的数据</p><h4 id="在接口所在机器上创建仓库镜像"><a href="#在接口所在机器上创建仓库镜像" class="headerlink" title="在接口所在机器上创建仓库镜像"></a>在接口所在机器上创建仓库镜像</h4><ol><li>clone代码</li></ol><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="keyword">clone</span> <span class="title">--mirror</span> git@git.oschina.net:demo/demo.git</span><br></pre></td></tr></table></figure><p>关于—mirror参数，请查看文章最尾部链接</p><ol><li>a</li></ol><figure class="highlight armasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="symbol">cd</span> demo.git/</span><br><span class="line"><span class="symbol">git</span> remote <span class="keyword">add </span>aliyun git<span class="comment">@code.aliyun.com:demo/demo.git # 这里对应你在阿里code上创建的仓库</span></span><br><span class="line"># 将OSCgit仓库的test和master分支分别推到阿里<span class="meta">code</span>，第一次初始化的时候必须这么推，以后只需要执行`git <span class="keyword">push </span>aliyun `即可。</span><br><span class="line"><span class="symbol">git</span> <span class="keyword">push </span>aliyun master</span><br><span class="line"><span class="symbol">git</span> <span class="keyword">push </span>aliyun test</span><br></pre></td></tr></table></figure><h4 id="使用Nodejs提供webhooks所需接口"><a href="#使用Nodejs提供webhooks所需接口" class="headerlink" title="使用Nodejs提供webhooks所需接口"></a>使用Nodejs提供webhooks所需接口</h4><p>这里使用express开发，比较简便，关于express入门请查看我的另一篇博客<a href="https://dev-heaven.com/2017/01/06/NodeEE%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/" target="_blank" rel="noopener">https://dev-heaven.com/2017/01/06/NodeEE%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/</a></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">npm install express-generator -g</span><br><span class="line">express webhook</span><br><span class="line">vim webhook/routes/index.js</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> express = <span class="keyword">require</span>(<span class="string">'express'</span>);</span><br><span class="line"><span class="keyword">var</span> exec = <span class="keyword">require</span>(<span class="string">'child_process'</span>).exec;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> router = express.Router();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> GIT_PATH = <span class="string">'/usr/local/src/huweirenmai.git/'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* GET home page. */</span></span><br><span class="line">router.get(<span class="string">'/'</span>, <span class="function"><span class="keyword">function</span><span class="params">(req, res, next)</span> </span>&#123;</span><br><span class="line">  res.render(<span class="string">'index'</span>, &#123; title: <span class="string">'Express'</span> &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">router.post(<span class="string">'/webhook'</span>, <span class="function"><span class="keyword">function</span><span class="params">(req, res, next)</span> </span>&#123;</span><br><span class="line">  <span class="comment">//var ref = req.body.ref;</span></span><br><span class="line">  <span class="comment">//var branch = ref.substring(ref.lastIndexOf("/") + 1);</span></span><br><span class="line">  <span class="keyword">var</span> commands = [</span><br><span class="line">      <span class="string">'cd '</span> + GIT_PATH,</span><br><span class="line">      <span class="string">'git fetch,</span></span><br><span class="line"><span class="string">      '</span>git push aliyun<span class="string">'</span></span><br><span class="line"><span class="string">    ].join('</span> &amp;&amp; <span class="string">');</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">  exec(commands, function(err, out, code) &#123;</span></span><br><span class="line"><span class="string">      if (err instanceof Error) &#123;</span></span><br><span class="line"><span class="string">        res.status(500).end('</span>Server Internal Error.<span class="string">');</span></span><br><span class="line"><span class="string">        throw err;</span></span><br><span class="line"><span class="string">      &#125;</span></span><br><span class="line"><span class="string">      //process.stderr.write(err);</span></span><br><span class="line"><span class="string">      //process.stdout.write(out);</span></span><br><span class="line"><span class="string">      res.status(200).end('</span>OK<span class="string">');</span></span><br><span class="line"><span class="string">  &#125;);  </span></span><br><span class="line"><span class="string">&#125;);</span></span><br><span class="line"><span class="string">module.exports = router;</span></span><br></pre></td></tr></table></figure><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ol><li>git-mirror <a href="http://blog.csdn.net/agul_/article/details/7843678" target="_blank" rel="noopener">http://blog.csdn.net/agul_/article/details/7843678</a></li><li><a href="https://help.aliyun.com/document_detail/30513.html?spm=5176.doc30516.6.543.cigGFK" target="_blank" rel="noopener">https://help.aliyun.com/document_detail/30513.html?spm=5176.doc30516.6.543.cigGFK</a></li></ol><p>全文完！</p></div><p class="readmore"><a href="/posts/55163.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/6546.html">ubuntu初始化配置</a></h1><div class="post-meta">2017-07-11</div><div class="post-content"><p>先备份/etc/apt/source.list，新建source.list，添加</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">deb <span class="string">http:</span><span class="comment">//mirrors.zju.edu.cn/ubuntu yakkety main universe restricted multiverse</span></span><br><span class="line">deb <span class="string">http:</span><span class="comment">//mirrors.zju.edu.cn/ubuntu yakkety-security main universe restricted multiverse</span></span><br><span class="line">deb <span class="string">http:</span><span class="comment">//mirrors.zju.edu.cn/ubuntu yakkety-updates main universe restricted multiverse</span></span><br><span class="line">deb <span class="string">http:</span><span class="comment">//mirrors.zju.edu.cn/ubuntu yakkety-backports main universe restricted multiverse</span></span><br><span class="line">deb-src <span class="string">http:</span><span class="comment">//mirrors.zju.edu.cn/ubuntu yakkety main universe restricted multiverse</span></span><br><span class="line">deb-src <span class="string">http:</span><span class="comment">//mirrors.zju.edu.cn/ubuntu yakkety-security main universe restricted multiverse</span></span><br><span class="line">deb-src <span class="string">http:</span><span class="comment">//mirrors.zju.edu.cn/ubuntu yakkety-updates main universe restricted multiverse</span></span><br><span class="line">deb-src <span class="string">http:</span><span class="comment">//mirrors.zju.edu.cn/ubuntu yakkety-backports main universe restricted multiverse</span></span><br></pre></td></tr></table></figure><p>apt-get update</p><p>安装gitlab<br>添加清华大学镜像</p><p>vim /etc/apt/source.list.d/gitlab-ce.list</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">deb</span> http<span class="variable">s:</span>//mirrors.tuna.tsinghua.edu.<span class="keyword">cn</span>/gitlab-<span class="keyword">ce</span>/ubuntu xenial main</span><br></pre></td></tr></table></figure></div><p class="readmore"><a href="/posts/6546.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/1914.html">RabbitMQ从入门到精通—安装</a></h1><div class="post-meta">2017-05-08</div><div class="post-content"><h5 id="安装脚本"><a href="#安装脚本" class="headerlink" title="安装脚本"></a>安装脚本</h5><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">yum install wget -y</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#Download</span> RabbitMQ and Erlang</span><br><span class="line">wget https:<span class="comment">//www.rabbitmq.com/releases/rabbitmq-server/v3.6.9/rabbitmq-server-3.6.9-1.el6.noarch.rpm</span></span><br><span class="line">wget https:<span class="comment">//www.rabbitmq.com/releases/erlang/erlang-19.0.4-1.el6.x86_64.rpm</span></span><br><span class="line"></span><br><span class="line"><span class="selector-id">#Install</span> Erlang and RabbitMQ</span><br><span class="line">rpm -ivh erlang-<span class="number">19.0</span>.<span class="number">4</span>-<span class="number">1</span><span class="selector-class">.el6</span><span class="selector-class">.x86_64</span><span class="selector-class">.rpm</span> </span><br><span class="line">rpm --import http:<span class="comment">//www.rabbitmq.com/rabbitmq-signing-key-public.asc</span></span><br><span class="line">yum install rabbitmq-server-<span class="number">3.6</span>.<span class="number">9</span>-<span class="number">1</span><span class="selector-class">.el6</span><span class="selector-class">.noarch</span><span class="selector-class">.rpm</span> -y</span><br></pre></td></tr></table></figure><h5 id="发现错误"><a href="#发现错误" class="headerlink" title="发现错误"></a>发现错误</h5><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1. </span>安装依赖Requires: socat</span><br><span class="line"><span class="bullet">2. </span>安装socat的时候提示需要tcp_wrappers</span><br></pre></td></tr></table></figure><h5 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h5><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">wget –no-cache http:<span class="regexp">//</span>www.convirture.com<span class="regexp">/repos/</span>definitions<span class="regexp">/rhel/</span><span class="number">6</span>.x<span class="regexp">/convirt.repo -O /</span>etc<span class="regexp">/yum.repos.d/</span>convirt.repo</span><br><span class="line">yum install socat -y</span><br></pre></td></tr></table></figure><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">使用http<span class="variable">s:</span>//rpmfind.net/linux/rpm2html/<span class="built_in">search</span>.php搜索tcp_wrappers，下载，rpm -ivh tcp_wrappers.<span class="keyword">version</span></span><br></pre></td></tr></table></figure><h5 id="启动RabbitMQ"><a href="#启动RabbitMQ" class="headerlink" title="启动RabbitMQ"></a>启动RabbitMQ</h5><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">chkconfig rabbitmq-server on</span><br><span class="line">/sbin<span class="built_in">/service </span>rabbitmq-server start</span><br><span class="line"><span class="comment"># 修改密码</span></span><br><span class="line">rabbitmqctl change_password guest welcome123</span><br><span class="line"><span class="comment"># 启动管理界面</span></span><br><span class="line">rabbitmq-plugins <span class="builtin-name">enable</span> rabbitmq_management</span><br></pre></td></tr></table></figure><h5 id="使用过程错误小记"><a href="#使用过程错误小记" class="headerlink" title="使用过程错误小记"></a>使用过程错误小记</h5><h6 id="user-‘guest’-User-can-only-log-in-via-localhost"><a href="#user-‘guest’-User-can-only-log-in-via-localhost" class="headerlink" title="user ‘guest’ - User can only log in via localhost"></a>user ‘guest’ - User can only log in via localhost</h6><p>登录响应如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"error"</span>:<span class="string">"not_authorised"</span>,<span class="attr">"reason"</span>:<span class="string">"User can only log in via localhost"</span>&#125;</span><br></pre></td></tr></table></figure><p>我实在docker中创建rabbitmq的，然后将端口15672映射到宿主机器上的15672，所以也算是远程登录，而guest用户要求只能使用localhost登录，所以要想远程登录必须新建用户。</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl <span class="keyword">add_user </span>&#123;username&#125; &#123;password&#125;</span><br><span class="line"><span class="comment"># 例：创建admin用户</span></span><br><span class="line">rabbitmqctl <span class="keyword">add_user </span>admin <span class="number">123456</span></span><br></pre></td></tr></table></figure><h6 id="新建用户无法登录"><a href="#新建用户无法登录" class="headerlink" title="新建用户无法登录"></a>新建用户无法登录</h6><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"error"</span>:<span class="string">"not_authorised"</span>,<span class="attr">"reason"</span>:<span class="string">"Not management user"</span>&#125;</span><br></pre></td></tr></table></figure><p>需要授予用户角色</p><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl <span class="keyword">set</span>_user_tags &#123;username&#125; &#123;role&#125;</span><br><span class="line"><span class="comment"># 例</span></span><br><span class="line">rabbitmqctl <span class="keyword">set</span>_user_tags admin administrator</span><br></pre></td></tr></table></figure><h6 id="用户授权"><a href="#用户授权" class="headerlink" title="用户授权"></a>用户授权</h6><figure class="highlight mathematica"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl set_permissions [-p vhostpath] &#123;user&#125; &#123;conf&#125; &#123;write&#125; &#123;read&#125;</span><br><span class="line">rabbitmqctl set_permissions -p / admin <span class="string">".*"</span> <span class="string">".*"</span> <span class="string">".*"</span></span><br></pre></td></tr></table></figure><h5 id="关于RabbitMQ权限和角色管理"><a href="#关于RabbitMQ权限和角色管理" class="headerlink" title="关于RabbitMQ权限和角色管理"></a>关于RabbitMQ权限和角色管理</h5><p>参考：<a href="http://blog.csdn.net/zyz511919766/article/details/42292655" target="_blank" rel="noopener">http://blog.csdn.net/zyz511919766/article/details/42292655</a></p><h4 id="trace"><a href="#trace" class="headerlink" title="trace"></a>trace</h4><p>调试消息的时候发现有些消息发出去了直接被丢弃了，但是也不知道怎么解决，这个时候其实可以打开log</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rabbitmq-plugins list</span><br><span class="line">rabbitmqctl <span class="builtin-name">enable</span> rabbitmq_tracing</span><br></pre></td></tr></table></figure></div><p class="readmore"><a href="/posts/1914.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/17775.html">MySQL时区问题</a></h1><div class="post-meta">2017-05-06</div><div class="post-content"><h3 id="现象"><a href="#现象" class="headerlink" title="现象"></a>现象</h3><p>MySQL中村的数据是<code>2017-05-05 21:23:58</code>，mybatis读取出来的时间是<code>2017-05-06 10:23:58</code>，我估计是Navicat根据当前系统时区自动做了转换，真正的时间其实还是<code>2017-05-06 10:23:58</code>，关于详细的说明，请参考第二条参考链接</p><h3 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h3><blockquote><p>根据现象描述，这个问题是由于MySQL时区问题导致的，要解决当然要修改时区了</p></blockquote><h4 id="自行安装MySQL"><a href="#自行安装MySQL" class="headerlink" title="自行安装MySQL"></a>自行安装MySQL</h4><h5 id="查看MySQL时区"><a href="#查看MySQL时区" class="headerlink" title="查看MySQL时区"></a>查看MySQL时区</h5><figure class="highlight asciidoc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&gt; select now();</span><br><span class="line"><span class="code">+-----------+</span></span><br><span class="line">| now()     |</span><br><span class="line"><span class="code">+-----------+</span></span><br><span class="line">| 22:27:19  |</span><br><span class="line"><span class="code">+-----------+</span></span><br><span class="line">&gt; show variables like "%time<span class="emphasis">_zone%";</span></span><br><span class="line"><span class="emphasis">+------------------+--------+</span></span><br><span class="line"><span class="emphasis">| Variable_</span>name    | Value  |</span><br><span class="line"><span class="code">+------------------+</span>--------+</span><br><span class="line">| system<span class="emphasis">_time_</span>zone | CST    |</span><br><span class="line">| time<span class="emphasis">_zone        | SYSTEM |</span></span><br><span class="line"><span class="emphasis">+------------------+--------+</span></span><br><span class="line"><span class="emphasis">2 rows in set (0.00 sec)</span></span><br><span class="line"><span class="emphasis">#time_</span>zone说明mysql使用system的时区，system<span class="emphasis">_time_</span>zone说明system使用CST时区</span><br></pre></td></tr></table></figure><h5 id="修改MySQL时区"><a href="#修改MySQL时区" class="headerlink" title="修改MySQL时区"></a>修改MySQL时区</h5><p>包含以下两种方法：</p><ul><li><p>临时修改（MySQL重启后失效）</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> <span class="keyword">global</span> <span class="keyword">time_zone</span> = <span class="string">'+8:00'</span>; <span class="comment">#修改mysql全局时区为北京时间，即我们所在的东8区</span></span><br><span class="line"><span class="keyword">set</span> <span class="keyword">time_zone</span> = <span class="string">'+8:00'</span>; <span class="comment">#修改当前会话时区</span></span><br><span class="line"><span class="keyword">flush</span> <span class="keyword">privileges</span>; <span class="comment">#立即生效</span></span><br></pre></td></tr></table></figure></li><li><p>修改my.cnf</p><figure class="highlight vala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/my.cnf</span><br><span class="line"><span class="meta">#在[mysqld]区域中加上</span></span><br><span class="line"><span class="keyword">default</span>-time_zone = <span class="string">'+8:00'</span></span><br><span class="line"><span class="meta"># /etc/init.d/mysqld restart #重启mysql使新时区生效</span></span><br></pre></td></tr></table></figure></li></ul><h4 id="阿里云RDS-MySQL5-7以下"><a href="#阿里云RDS-MySQL5-7以下" class="headerlink" title="阿里云RDS(MySQL5.7以下)"></a>阿里云RDS(MySQL5.7以下)</h4><p><strong>注意：只有RDS-MySQL5.5、5.6支持</strong></p><p>参考：<a href="https://help.aliyun.com/knowledge_detail/41689.html" target="_blank" rel="noopener">https://help.aliyun.com/knowledge_detail/41689.html</a></p><h4 id="阿里云RDS-MySQL5-7"><a href="#阿里云RDS-MySQL5-7" class="headerlink" title="阿里云RDS(MySQL5.7)"></a>阿里云RDS(MySQL5.7)</h4><blockquote><p>阿里云RDS MySQL5.7不支持修改服务器参数</p></blockquote><p>这个时候我们可以从代码层面修正时区，可以使用在jdbc链接中指定时区参数<code>serverTimezone=Asia/Shanghai</code></p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">database.url</span>=jdbc:mysql://rm-abcdefghijkl.mysql.rds.aliyuncs.com:<span class="number">3306</span>/mydb?serverTimezone=Asia/Shanghai&amp;autoReconnect=<span class="literal">true</span>&amp;failOverReadOnly=<span class="literal">false</span>&amp;useSSL=<span class="literal">false</span>&amp;allowMultiQueries=<span class="literal">true</span></span><br></pre></td></tr></table></figure><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="http://www.jb51.net/article/84198.htm" target="_blank" rel="noopener">http://www.jb51.net/article/84198.htm</a></li><li><a href="http://www.jianshu.com/p/3dbccdef6031" target="_blank" rel="noopener">http://www.jianshu.com/p/3dbccdef6031</a></li></ul></div><p class="readmore"><a href="/posts/17775.html">Leer más</a></p></div><div class="post"><h1 class="post-title"><a href="/posts/40756.html">Docker小试牛刀--在docker中安装centos</a></h1><div class="post-meta">2017-04-21</div><div class="post-content"><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">docker pull centos:6 <span class="comment">#使用6.x版本的centos,7.x目前有问题（无法使用正在的root权限）</span></span><br><span class="line">docker run -t -i centos /bin/bash <span class="comment">#创建container from centos image</span></span><br><span class="line">passwd  <span class="comment"># 这个命令用于修改root用户密码</span></span><br><span class="line">yum <span class="keyword">install</span> openssh-<span class="keyword">server</span> -y</span><br><span class="line">yum <span class="keyword">install</span> openssh-clients -y</span><br><span class="line">vi /etc/ssh/sshd_config <span class="comment">#编辑sshd配置，去掉PermitRootLogin yes的注释</span></span><br><span class="line">service sshd restart <span class="comment">#启动sshd</span></span><br><span class="line">chkconfig sshd <span class="keyword">on</span>   <span class="comment">#sshd开机重启</span></span><br><span class="line">docker ps -a <span class="comment"># 查看容器id</span></span><br><span class="line">docker <span class="keyword">commit</span> &lt;<span class="keyword">CONTAINER</span> <span class="keyword">ID</span>&gt; &lt;自定义镜像名&gt; <span class="comment"># 如 docker commit 9971b45dc32b jarvan4dev/utility</span></span><br><span class="line">docker images <span class="comment"># 查看镜像，可以看到自己刚才创建的镜像 jarvan4dev/utility</span></span><br><span class="line">docker run <span class="comment">--privileged -d -i -t -p 22222:22 -v /Users/jarvan4dev/Documents/docker:/home jarvan4dev/utility /usr/sbin/sshd -D</span></span><br><span class="line">docker ps -a <span class="comment"># 查看所有容器，看看容器状态，确认为Up后即可</span></span><br><span class="line">ssh root@localhost -p <span class="number">22222</span> <span class="comment"># 登录容器</span></span><br></pre></td></tr></table></figure><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#动态添加端口映射</span></span><br><span class="line">iptables -t<span class="built_in"> nat </span>-A  DOCKER -p tcp --dport 8088 -j DNAT --to-destination 172.17.0.2:80</span><br></pre></td></tr></table></figure><h4 id="解释"><a href="#解释" class="headerlink" title="解释"></a>解释</h4><p>—privileged 是为了让容器中的root用户获取真正的root权限<br>-d demon运行<br>-t Allocate a pseudo-TTY<br>-i Keep STDIN open even if not attached<br>-p 端口映射 &lt;宿主机端口&gt;:&lt;容器中机器端口&gt;<br>-v 目录映射 &lt;宿主机目录&gt;:&lt;容器中机器目录&gt;<br>-D 这个参数不是docker命令参数，是sshd的，注意和docker的-d区分</p><h4 id="坑"><a href="#坑" class="headerlink" title="坑"></a>坑</h4><p>第一次运行镜像后，然后删除容器，再次运行镜像，重新登录可能会出现如下错误：</p><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@</span><br><span class="line">@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @</span><br><span class="line">@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@</span><br><span class="line">IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!</span><br><span class="line">Someone could be eavesdropping on you right now (man-<span class="keyword">in</span>-the-middle attack)!</span><br><span class="line">It is also possible that a host key has just been changed.</span><br><span class="line">The fingerprint <span class="keyword">for</span> the RSA key sent by the remote host is</span><br><span class="line">SHA256:VOHZJ5i80esyqbm+aORZbPI6GhcXYEkTKfp4L4+v+<span class="number">9</span>A.</span><br><span class="line">Please contact your <span class="keyword">system</span> administrator.</span><br><span class="line">Add correct host key <span class="keyword">in</span> /Users/jarvan4dev/.ssh/known_hosts to get rid of this message.</span><br><span class="line">Offending RSA key <span class="keyword">in</span> /Users/jarvan4dev/.ssh/known_hosts:<span class="number">19</span></span><br><span class="line">RSA host key <span class="keyword">for</span> [localhost]:<span class="number">22222</span> has changed and you have requested strict checking.</span><br><span class="line">Host key verification failed.</span><br></pre></td></tr></table></figure><p>解决： 删除 ~/.ssh/know_hosts文件中的 localhost:22222即可</p></div><p class="readmore"><a href="/posts/40756.html">Leer más</a></p></div><nav class="page-navigator"><a class="extend prev" rel="prev" href="/">Anterior</a><a class="page-number" href="/">1</a><span class="page-number current">2</span><a class="page-number" href="/page/3/">3</a><a class="page-number" href="/page/4/">4</a><a class="page-number" href="/page/5/">5</a><a class="extend next" rel="next" href="/page/3/">Siguiente</a></nav></div></div><div class="pure-u-1-4 hidden_mid_and_down"><div id="sidebar"><div class="widget"><form class="search-form" action="//www.google.com/search" method="get" accept-charset="utf-8" target="_blank"><input type="text" name="q" maxlength="20" placeholder="Search"><input type="hidden" name="sitesearch" value="https://www.dev-heaven.com"></form></div><div class="widget"><div class="widget-title"><i class="fa fa-folder-o">Categorías</i></div><ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/博客/">博客</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/生活/">生活</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/随笔/">随笔</a></li></ul></div><div class="widget"><div class="widget-title"><i class="fa fa-star-o">Etiquetas</i></div><div class="tagcloud"><a href="/tags/springMVC/" style="font-size:15px">springMVC</a> <a href="/tags/docker/" style="font-size:15px">docker</a> <a href="/tags/express/" style="font-size:15px">express</a> <a href="/tags/POI/" style="font-size:15px">POI</a> <a href="/tags/Java/" style="font-size:15px">Java</a> <a href="/tags/Linux/" style="font-size:15px">Linux</a> <a href="/tags/firewall/" style="font-size:15px">firewall</a> <a href="/tags/MySQL/" style="font-size:15px">MySQL</a> <a href="/tags/mybatis/" style="font-size:15px">mybatis</a> <a href="/tags/Ngrok/" style="font-size:15px">Ngrok</a> <a href="/tags/微信/" style="font-size:15px">微信</a> <a href="/tags/ECS/" style="font-size:15px">ECS</a> <a href="/tags/RabbitMQ/" style="font-size:15px">RabbitMQ</a> <a href="/tags/Nginx/" style="font-size:15px">Nginx</a> <a href="/tags/域名/" style="font-size:15px">域名</a> <a href="/tags/Nodejs/" style="font-size:15px">Nodejs</a> <a href="/tags/nvm/" style="font-size:15px">nvm</a> <a href="/tags/SpringBoot/" style="font-size:15px">SpringBoot</a> <a href="/tags/log4j2/" style="font-size:15px">log4j2</a> <a href="/tags/Docker/" style="font-size:15px">Docker</a> <a href="/tags/x-www-form-urlencoded/" style="font-size:15px">x-www-form-urlencoded</a> <a href="/tags/form-data/" style="font-size:15px">form-data</a> <a href="/tags/axios/" style="font-size:15px">axios</a> <a href="/tags/css/" style="font-size:15px">css</a> <a href="/tags/git/" style="font-size:15px">git</a> <a href="/tags/life/" style="font-size:15px">life</a> <a href="/tags/health/" style="font-size:15px">health</a> <a href="/tags/npm/" style="font-size:15px">npm</a> <a href="/tags/nodejs/" style="font-size:15px">nodejs</a> <a href="/tags/nrm/" style="font-size:15px">nrm</a> <a href="/tags/redis/" style="font-size:15px">redis</a> <a href="/tags/Spring/" style="font-size:15px">Spring</a> <a href="/tags/spring-data-redis/" style="font-size:15px">spring-data-redis</a> <a href="/tags/pyenv/" style="font-size:15px">pyenv</a> <a href="/tags/Python/" style="font-size:15px">Python</a> <a href="/tags/ReentrantLock/" style="font-size:15px">ReentrantLock</a> <a href="/tags/https/" style="font-size:15px">https</a> <a href="/tags/poet/" style="font-size:15px">poet</a> <a href="/tags/shell/" style="font-size:15px">shell</a></div></div><div class="widget"><div class="widget-title"><i class="fa fa-file-o">Recientes</i></div><ul class="post-list"><li class="post-list-item"><a class="post-list-link" href="/posts/48761.html">关于ReentrantLock实现的一点思考</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/61735.html">Java内存优化之POI Excel（三）</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/64862.html">Java内存优化之POI Excel（二）</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/27940.html">Java内存优化之POI Excel（一）</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/955.html">最佳实践之Java基础</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/49259.html">Vue + SpringBoot 小问题记录</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/43546.html">git 删除远程分支</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/37317.html">Java规范笔记---读《阿里巴巴Java开发手册》</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/56838.html">Java获取泛型的真实类型</a></li><li class="post-list-item"><a class="post-list-link" href="/posts/6407.html">RabbitMQ从入门到精通—publish confirm机制</a></li></ul></div><div class="widget"><div class="widget-title"><i class="fa fa-external-link">Blogroll</i></div><ul></ul><a href="http://www.example1.com/" title="site-name1" target="_blank">site-name1</a><ul></ul><a href="http://www.example2.com/" title="site-name2" target="_blank">site-name2</a><ul></ul><a href="http://www.example3.com/" title="site-name3" target="_blank">site-name3</a></div></div></div><div class="pure-u-1 pure-u-md-3-4"><div id="footer">Copyright © 2019 <a href="/." rel="nofollow">菜鸟IT路.</a> Powered by<a rel="nofollow" target="_blank" href="https://hexo.io"> Hexo.</a><a rel="nofollow" target="_blank" href="https://github.com/tufu9441/maupassant-hexo"> Theme</a> by<a rel="nofollow" target="_blank" href="https://github.com/pagecho"> Cho.</a></div></div></div><a class="show" id="rocket" href="#top"></a><script type="text/javascript" src="/js/totop.js?v=0.0.0" async></script><script type="text/javascript" src="//lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js" async></script><script type="text/javascript" src="/js/fancybox.js?v=0.0.0" async></script><link rel="stylesheet" type="text/css" href="//lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css"><script type="text/javascript" src="/js/codeblock-resizer.js?v=0.0.0"></script><script type="text/javascript" src="/js/smartresize.js?v=0.0.0"></script></div></body></html>